package partitions;

import hips.region.NewRegionEvent;
import hips.region.NewRegionListener;
import hips.Image;
import hips.Partition;
import hips.PixelValue;
import hips.Region;
import hips.gui.ComponentLocation;
import hips.images.floating.gray32.ImageGray32;
import hips.images.integer.gray16.ImageGray16;
import hips.images.integer.gray8.ImageGray8;
import hips.images.integer.rgb.ImageRGB;
import ij.gui.StackWindow;
import ij.plugin.Slicer;

import java.awt.Container;
import java.awt.Scrollbar;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.text.DecimalFormat;
import java.util.Date;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class Listener implements WindowListener, MouseListener, MouseMotionListener, MouseWheelListener, NewRegionListener, AdjustmentListener, ChangeListener {

	private static long ELAPSED = 50;
	private long time;
	private Partition p;
	private InfoPanel info;
	private Image backup;
	private Image f;
	private int method;
	private PixelValue alpha;
	private PixelValue omega;
	private float cindex;
	private int randomPixels;
	private boolean highlightedPinned;
	private Integer lastPixel;
	private Integer lastX;
	private Integer lastY;
	private int iterations;
	private int previousIterations;
	private MainFrame frame;

	public Image output;

	public Listener(Image f, Image output, MainFrame frame, int method, PixelValue alpha, PixelValue omega, float cindex, int randomPixels, int iterations, int previousIterations) {
		this.f = f;
		this.output = output;
		this.frame = frame;
		this.method = method;
		this.alpha = alpha;
		this.omega = omega;
		this.cindex = cindex;
		this.randomPixels = randomPixels;
		this.iterations = iterations;
		this.previousIterations = previousIterations;
	}

	public void start() {
		if (output == null) {
			output = f.newImage();
			output.getImagePlus().show();
			ComponentLocation.setLocationCenter(0.75f, 0.5f, output.getImagePlus().getWindow());
		}
		lastPixel = null;
		lastX = null;
		lastY = null;
		p = f.newPartition(method, alpha, omega, cindex, randomPixels);
		time = System.currentTimeMillis();
		makeRegions();
		time = System.currentTimeMillis() - time;
		createInfoPanel(alpha.maxValue(), omega, cindex, randomPixels); // LINEA
																		// A
																		// CAMBIARRRRRRRRR
		if (output.getImagePlus().getStackSize() > 1) {
			StackWindow stack = (StackWindow) output.getImagePlus().getWindow();
			Container c = (Container) stack.getComponent(1);
			Scrollbar s = (Scrollbar) c.getComponent(1);
			s.addAdjustmentListener(this);
		}
		output.getImagePlus().getCanvas().addMouseListener(this);
		output.getImagePlus().getCanvas().addMouseMotionListener(this);
		output.getImagePlus().getWindow().addWindowListener(this);
		output.getImagePlus().getWindow().addMouseWheelListener(this);
		frame.allSlices.addChangeListener(this);
		highlightedPinned = false;
	}

	public void continueIterating() {
		closing();
		frame.previousIterations = iterations + previousIterations;
		frame.outputImage = output;
		frame.parameters.setVisible(true);
		frame.setEnabled(false);
	}

	private void createInfoPanel(Number alpha, PixelValue omega, float cindex, int randomPixels) {
		info = new InfoPanel(this);
		frame.tabs.addChangeListener(this);
		frame.tabs.add(info);
		frame.tabs.setTitleAt(frame.tabs.getTabCount() - 1, " Partition " + frame.tabs.getTabCount() + " ");
		frame.tabs.setSelectedIndex(frame.tabs.getTabCount() - 1);
		info.cindex.setText("" + cindex);
		info.pixels.setText("" + f.getSize());
		info.regions.setText("" + p.getRegionSize());
		info.elapsed.setText(formattedTime(time));
		info.elapsed2.setText("" + formattedTime(time));
		info.channels.setText("" + f.getChannels());
		info.slices.setText("" + f.getSlices());
		info.dimensions.setText("" + f.getWidth() + " x " + f.getHeight());
		info.title.setText(f.getTitle());
		info.localRange.setText(formattedLocalRange(alpha));
		info.random.setText("" + randomPixels);
		info.iterations.setText("" + (iterations + previousIterations));
		if (f instanceof ImageGray8) {
			info.type.setText("8-bit Gray");
		}
		if (f instanceof ImageGray16) {
			info.type.setText("16-bit Gray");
		}
		if (f instanceof ImageGray32) {
			info.type.setText("32-bit Gray (floating point)");
		}
		if (f instanceof ImageRGB) {
			info.type.setText("24-bit RGB");
		}
		if (p.getMethod() == Partition.IMPROVED_WITHOUT_CINDEX) {
			info.method.setText("Improved, without c. index");
		}
		if (p.getMethod() == Partition.IMPROVED_WITH_CINDEX) {
			info.method.setText("Improved, with c. index");
		}
		if (p.getMethod() == Partition.ORIGINAL) {
			info.method.setText("Original");
		}
		setSliceInfo();
		setPixelInfo();
	}

	private void setSliceInfo() {
		if (f.getSlices() > 1 && !frame.allSlices.isSelected()) {
			int slice = output.getImagePlus().getSlice() - 1;
			info.globalRange.setText(p.getOmega().getString(slice));
			info.localRange.setText(p.getAlpha().getString(slice));
		} else {
			info.globalRange.setText(p.getOmega().getString());
			info.localRange.setText(p.getAlpha().getString());
		}
	}

	private void setPixelInfo() {
		if (lastPixel == null) {
			info.inputValue.setText("");
			info.outputValue.setText("");
			info.outputValue2.setText("");
			info.pixel.setText("");
			info.regionId.setText("");
			info.regionSize.setText("");
			return;
		}
		if (f.getSlices() > 1 && !frame.allSlices.isSelected()) {
			int slice = output.getImagePlus().getSlice() - 1;
			PixelValue pv = f.getPixelValue(lastPixel);
			info.inputValue.setText(pv.getString(slice));
			output.getPixelValue(pv, lastPixel);
			info.outputValue.setText(pv.getString(slice));
			info.outputValue2.setText(pv.getString(slice));
		} else {
			PixelValue pv = f.getPixelValue(lastPixel);
			info.inputValue.setText(pv.getString());
			output.getPixelValue(pv, lastPixel);
			info.outputValue.setText(pv.getString());
			info.outputValue2.setText(pv.getString());
		}
		info.pixel.setText(xy(lastX, lastY));
		Region region = p.getRegionByPixel(lastPixel);
		info.regionId.setText("" + region.getLabel());
		info.regionSize.setText("" + region.getSize());
	}

	private void makeRegions() {
		int n = iterations;
		p.addNewRegionEventListener(this);
		if (previousIterations == 0) {
			backup = f;
			p.makeRegions();
			n--;
		}
		if (n > 0) {
			backup = f.newImage();
			p.setImage(backup);
			for (int i = 0; i < n; i++) {
				if (p.getRegionSize() > 0) {
					for (int j = 0; j < p.getRegionSize(); j++) {
						Region region = p.getRegionByLabel(j);
						backup.paintRegion(region, output.getMeanValue(region));
					}
				} else {
					PixelValue p = output.newPixelValue();
					for (int j = 0; j < output.getSize(); j++) {
						backup.putPixelValue(j, output.getPixelValue(p, j));
					}
				}
				int previousSize = p.getRegionSize();
				p.makeRegions();
				if (p.getRegionSize() == previousSize) {
					iterations = i + 2;
					break;
				}
			}
		}
		p.removeNewRegionEventListener(this);
	}

	public void removeListeners() {
		try {
			frame.allSlices.removeChangeListener(this);
			output.setRegionOfInterest(null);
			frame.tabs.removeChangeListener(this);
			output.getImagePlus().getWindow().removeWindowListener(this);
			output.getImagePlus().getWindow().removeMouseWheelListener(this);
			if (output.getSlices() > 1) {
				StackWindow stack = (StackWindow) output.getImagePlus().getWindow();
				Container c = (Container) stack.getComponent(1);
				Scrollbar s = (Scrollbar) c.getComponent(1);
				s.removeAdjustmentListener(this);
			}
			output.getImagePlus().getCanvas().removeMouseListener(this);
			output.getImagePlus().getCanvas().removeMouseMotionListener(this);
		} catch (NullPointerException e) {
		}
	}

	public void adjustmentValueChanged(AdjustmentEvent e) {
		setSliceInfo();
	}

	public void mouseClicked(MouseEvent e) {
		if (e.getButton() != MouseEvent.BUTTON1) {
			return;
		}
		int x = output.getImagePlus().getCanvas().offScreenX(e.getX());
		int y = output.getImagePlus().getCanvas().offScreenY(e.getY());
		int pos = output.getWidth() * y + x;
		Region r = p.getRegionByPixel(pos);
		highlightedPinned = !highlightedPinned;
		if (!highlightedPinned) {
			output.setRegionOfInterest(r);
		}
	}

	public void mouseMoved(MouseEvent e) {
		int x = output.getImagePlus().getCanvas().offScreenX(e.getX());
		int y = output.getImagePlus().getCanvas().offScreenY(e.getY());
		int pos = output.getWidth() * y + x;
		Region r = p.getRegionByPixel(pos);
		if (!highlightedPinned) {
			output.setRegionOfInterest(r);
		}
		if ((new Date()).getTime() - time > ELAPSED) {
			lastPixel = pos;
			lastX = x;
			lastY = y;
			setPixelInfo();
			time = System.currentTimeMillis();
		}
	}

	private void closing() {
		removeListeners();
		for (int i = 0; i < frame.tabs.getTabCount(); i++) {
			if (frame.tabs.getComponent(i) == info) {
				frame.tabs.remove(i);
				break;
			}
		}
		for (int i = 0; i < frame.listenerList.size(); i++) {
			if (frame.listenerList.get(i) == this) {
				frame.listenerList.remove(i);
				break;
			}
		}
	}

	public void windowClosed(WindowEvent e) {
		closing();
	}

	public void windowActivated(WindowEvent e) {
		for (int i = 0; i < frame.tabs.getTabCount(); i++) {
			if (frame.tabs.getComponent(i) == info) {
				frame.tabs.setSelectedIndex(i);
				break;
			}
		}
	}

	public static String xy(int x, int y) {
		return "(" + x + ",   " + y + ")";
	}

	public static String formattedTime(long time) {
		int minutes = (int) (time / 60000);
		float seconds = (time / 1000f - minutes * 60);
		String stringTime = "";
		if (minutes > 0) {
			stringTime += minutes + " min  ";
		}
		return stringTime + new DecimalFormat("0.#").format(seconds).replace(",", ".") + " sec";
	}

	public static String formattedLocalRange(Number alpha) {
		if (alpha instanceof Integer) {
			return alpha.toString();
		}
		return new DecimalFormat("0.0#").format(alpha).replace(",", ".");
	}

	public void newRegionCreated(NewRegionEvent e) {
		output.paintRegion(e.getRegion(), backup.getMeanValue(e.getRegion()));
		output.getImagePlus().updateAndDraw();
	}

	public void stateChanged(ChangeEvent e) {
		if (e.getSource() == frame.allSlices){
			setSliceInfo();
		}
		if (frame.tabs.getSelectedComponent() == info) {
			output.getImagePlus().getWindow().toFront();
		}
	}

	public void mouseWheelMoved(MouseWheelEvent e) {
		setSliceInfo();
	}

	public void mouseExited(MouseEvent e) {
		lastPixel = null;
		lastX = null;
		lastY = null;
		setPixelInfo();
		if (highlightedPinned) {
			return;
		}
		output.setRegionOfInterest(null);
	}

	public void mousePressed(MouseEvent e) {
	}

	public void mouseReleased(MouseEvent e) {
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseDragged(MouseEvent e) {
	}

	public void windowOpened(WindowEvent e) {
	}

	public void windowClosing(WindowEvent e) {
	}

	public void windowIconified(WindowEvent e) {
	}

	public void windowDeiconified(WindowEvent e) {
	}

	public void windowDeactivated(WindowEvent arg0) {
	}
}
